
// SAMPLE gui - ifdef
#ifdef USE_LOADGEN_GUI
#else
#endif // USE_LOADGEN_GUI

#include <iostream>


#ifdef USE_LOADGEN_GUI

    #include <QtDebug>
    #include <QObject>
    #include <QApplication>
    #include <QPixmap>
    #include <QSplashScreen>
    #include <QString>
    #include "ui_main.h"
    #include "fileio.h"

#else

    #include <boost/thread/xtime.hpp>
    #include "cmdinteraction.h"
#endif // USE_LOADGEN_GUI

#include "SingleCallDataVectorParser.h"
#include "Oxsipgen.h"
#include "extramethods.h"
#include "ParameterParser.h"
#include "eventhistory.h"


using std::string;
using std::cout;
using std::endl;
using std::cerr;
using std::vector;

extern void* (*pt2Object);

Oxsipgen* Oxsipgen::_theInstance=0;

bool parseArgs(int argc, char* argv[],
               string &filename)
{
    if ( argc == 2) 
    {
        filename = argv[1] ;
        return true;
    }
    else return false;
}

int main(int argc, char* argv[])
{

//    sipxConfigSetLogLevel(LOG_LEVEL_DEBUG) ;
//    sipxConfigSetLogFile("Oxsipgen.log");
    enableConsoleOutput(false) ;                // disables the debug output


    #ifdef USE_LOADGEN_GUI

    cout << "Gui is used !!!!!" << endl;
    QApplication app(argc, argv);

    Oxsipgen *ox = Oxsipgen::getInstance();
    ox->setQApplication(&app);

    return app.exec();
    #else

    string mainScenarioFilename;
    if (!parseArgs(argc, argv, mainScenarioFilename))
    {
        usage(argv[0]);
        exit(1);
    }
    Oxsipgen *ox = Oxsipgen::getInstance();
    ox->loadNewScene(mainScenarioFilename);
    ox->run();

    return 0;
    #endif // USE_LOADGEN_GUI


}

// Print usage message
void usage(const char* szExecutable)
{
    char szBuffer[64];

    cout << "\nUsage:\n" ;
    cout << "   "<< szExecutable << " <mainScene> \n" ;
    cout << "      using " << szBuffer << endl;
    cout << "\n" ;
    cout << "Options:\n" ;
    cout << "   <mainScene> configuration file for the main script that runs the different threads\n" ;
    cout << endl ;
}


Oxsipgen::Oxsipgen()
{
    // initializing of variables
    sceneLoaded=false;

    #ifdef USE_LOADGEN_GUI

    ui.setupUi(&window);
    QPixmap pixmap(":/splash.png");
    QSplashScreen splash(pixmap);
    splash.show();
    window.show();

    QObject::connect( ui.threadsTableWidget, SIGNAL(cellClicked ( int , int )),             // when clicking on row in ThreadsTable
                      ui.threadsTableWidget, SLOT(cellClicked (int, int)) );                //   just a slot that emits signal with other signature
    QObject::connect( ui.threadsTableWidget, SIGNAL(updateListOfElements ( int, bool)),     // updates the elements in SelectedThreadListWidget
                      ui.selectedThreadListWidget, SLOT(updateListOfElements(int , bool)) );

    QObject::connect( ui.threadsTableWidget, SIGNAL(changeAudioThread (const int)),         // changes the Audio channel
                      this, SLOT( changeAudioThread(const int )) );

        // MENU's in the GUI
    QObject::connect( ui.action_Force_Hangup , SIGNAL(triggered()),
                      this, SLOT(forceHangup()) );
    QObject::connect( ui.action_Quit_Kill, SIGNAL(triggered()),                             // EXIT => just exit
                      this, SLOT(quitkill()) );
    QObject::connect( ui.action_Quit_and_terminate_threads, SIGNAL(triggered()),            // EXIT => stop threads and exit
                      this, SLOT(quitfriendly()) );

    QObject::connect( ui.action_Open, SIGNAL(triggered()),
                      FileIO::getInstance(), SLOT(menuOpen()));
    QObject::connect( FileIO::getInstance(), SIGNAL(loadNewScene( const QString )),
                      this, SLOT(loadNewSceneSlot( const QString )));
    QObject::connect( ui.action_Start, SIGNAL(triggered()),
                      this, SLOT(runSlot()) );
    QObject::connect( ui.action_Save_History, SIGNAL(triggered()),
                      FileIO::getInstance(), SLOT(menuHistorySave()) );
    QObject::connect( ui.action_Save_All_Histories, SIGNAL(triggered()),
                      FileIO::getInstance(), SLOT(menuHistorySaveAll()) );

    QObject::connect( FileIO::getInstance(), SIGNAL(whatIsTheSelectedThreadId(int &)),
                      ui.selectedThreadListWidget, SLOT(getSelectedThreadId(int &)) );
    // statistics
/*    QObject::connect( this, SIGNAL()
                      ui.progressBar_percGoodCalls, SLOT(setValue(int)) );
*/
    sleep(2);
    splash.finish(&window);
    #else

    #endif // !USE_LOADGEN_GUI

}

Oxsipgen* Oxsipgen::getInstance()
{
    if (0 == _theInstance )
        _theInstance = new Oxsipgen();
    return _theInstance;
}

Oxsipgen::~Oxsipgen()
{
    sceneCleanup();

}
void Oxsipgen::operator()() 
{
    run();
}

void Oxsipgen::run()
{
    if (sceneLoaded) {

        int numberOfThreads = getNumberOfThreads();
        cout << "Oxsipgen::run() : numOfThreads : " << numberOfThreads << endl;
        for(int i=0; i<numberOfThreads;i++)
        {
            #ifdef USE_LOADGEN_GUI
            scArray.at(i)->start();                                         // START the thread
            #else
            thrds.create_thread(boost::ref( (scArray[i]).operator*() ));    // START the thread
            #endif // USE_LOADGEN_GUI
        }

        #ifdef USE_LOADGEN_GUI

        #else
        CmdInteraction cmd;                     // I think it's here that the CmdInteraction should be
        cmd.grabInput();                        // grab the input and wait for commands
        #endif // USE_LOADGEN_GUI
        waitForThreadsToTerminate();
    }
    else {
        cerr << "Scene was not loaded ! " << endl;
    }
}

void Oxsipgen::terminateAllCalls()
{
    for (uint i=0; i < getNumberOfThreads() ; i++ ) {
        if (!scArray[i]->isHangedUp()) scArray[i]->forceHangUp();
    }
}

void Oxsipgen::setAudioThread( const int thread )
{
    //std::cerr << "Oxsipgen::setAudioThread( const int thread )" <<endl;
    scArray[thread]->playAudio();
}


/**
 * Get (remote) a list of all the threads, their status, loop, filename
 */
vector<LOADGEN_SINGLECALL_STRUCT> Oxsipgen::getListOfThreadsInfo()
{
    vector<LOADGEN_SINGLECALL_STRUCT> list;
    int numberOfThreads = getNumberOfThreads();
    cout << "Oxsipgen::run() : numOfThreads : " << numberOfThreads << endl;
    for(int i=0; i<numberOfThreads;i++)
    {
        LOADGEN_SINGLECALL_STRUCT call;
        call.currentRepeat = scArray[i]->getCurrentRepeat();
        call.myNumber = scArray[i]->getMyNumber();
        call.filename =scArray[i]->getFilename();
        call.repeatCount = scArray[i]->getRepeatCount();
        call.state = scArray[i]->getState();
        list.push_back(call);
    }
    return list;
}

/**
 * Saves the eventHistory for every item in the array
 */
bool Oxsipgen::saveThreadsHistory(string filename, int threadId)
{
    vector<EventHistory*> evlist;
    if (-1 == threadId) // save all
    {
        for (uint i=0; i < getNumberOfThreads(); i++) {
            evlist.push_back(scArray[i]->getEventHistory());
        }
    }
    else {              // save only one
        evlist.push_back(scArray[threadId]->getEventHistory());
    }
    return EventHistory::saveEventHistoryItems(filename, evlist);
}

/**
 * This methods removes remaining things from the threads/calls so another session can be started
 * Beware that it does not check if the call is still running.
 */
void Oxsipgen::sceneCleanup()
{
    #ifdef USE_LOADGEN_GUI
    // cleans up vector by removing signals
    // then delete the contence of the pointer
    // and afterwards reset the vector
    // don't forget to reset the static counter of the singlecalls
    for (int i=0; i < scArray.size() ; i++ ) {
        QObject::disconnect( scArray.at(i), SIGNAL(updateListOfElements ( int, bool)),
                          ui.selectedThreadListWidget , SLOT(updateListOfElements(int , bool)) );
        QObject::disconnect( scArray.at(i), SIGNAL(updateThreadInfo( int, int, QString)),
                          ui.threadsTableWidget , SLOT(updateThreadOfTable( int, int, QString)) );
        delete scArray.at(i);
    }
    scArray.clear();
    SingleCall::resetNumberOfSingleCalls();
    // the gui also needs some cleanup
    ui.threadsTableWidget->cleanUpTable();
    ui.selectedThreadListWidget->cleanUpList();
    #else
    delete[] scArray;
    #endif // USE_LOADGEN_GUI

    sceneLoaded=false;
    delete pt2Object;
}

void Oxsipgen::loadNewScene(const string filename)
{
    if (sceneLoaded ) sceneCleanup();
    ParameterParser::getInstance()->loadNewFile(filename);
    changeToDirFromFilelocation(filename);
    int numberOfThreads = ParameterParser::getInstance()->getNumberOfThreads();
    #ifdef USE_LOADGEN_GUI
    scArray.reserve ( numberOfThreads ); // Preallocate the correct size for the vector
    #else
    scArray = new boost::shared_ptr<SingleCall>[ numberOfThreads ];
    #endif // USE_LOADGEN_GUI

    pt2Object = new void *[numberOfThreads];

    for(int i=0; i<numberOfThreads;i++)
    {
        string scen = ParameterParser::getInstance()->parseParameter("scene", i);
        if(scen == "")
        {
            std::cerr << "No scenario file found for thread: " << i << std::endl;
        }
        SingleCallDataVectorParser sCDVP(scen,i);
        vector<string> data = sCDVP.getDataVector();
        string repeats = ParameterParser::getInstance()->parseParameter("repeats", i);
        int repeatCount = 1;
        if(repeats == "")
        {
            std::cerr << "No number of repeats found for thread: " << i 
                    << " (setting to one)" << std::endl;
        }
        else
        {
            repeatCount = atoi(repeats.c_str());
        }
        SingleCall* sc_p =  new SingleCall(data,repeatCount, scen) ;

        // if(0==i) sc_p->playAudio();

        #ifdef USE_LOADGEN_GUI
        scArray.push_back( sc_p );
        #else
        scArray[i] = boost::shared_ptr<SingleCall> ( sc_p );
        #endif // USE_LOADGEN_GUI

        #ifdef USE_LOADGEN_GUI       // QT signals for that thread
        bool fitToContent=false;
        if ( i == (numberOfThreads-1) ) fitToContent=true;
        ui.threadsTableWidget->addNewThreadToTable(
                repeatCount,
                QString(ParameterParser::getInstance()->parseParameter("scene", i).c_str()),
                fitToContent);
        QObject::connect( scArray.at(i), SIGNAL(updateListOfElements ( int, bool)),
                          ui.selectedThreadListWidget , SLOT(updateListOfElements(int , bool)) );
        QObject::connect( scArray.at(i), SIGNAL(updateThreadInfo( int, int, QString)),
                          ui.threadsTableWidget , SLOT(updateThreadOfTable( int, int, QString)) );
        QObject::connect( scArray.at(i), SIGNAL(updateStatistics()),
                          this, SLOT(updateStatistics()) );
        #else
        // nothing to do, no signals in command line mode
        #endif // USE_LOADGEN_GUI
    }

    sceneLoaded=true;
}


EventHistory * Oxsipgen::getHistoryOfThread(int threadId)
{
    return scArray[threadId]->getEventHistory();
}


uint Oxsipgen::getNumberOfThreads()
{
    return ParameterParser::getInstance()->getNumberOfThreads();
}
void Oxsipgen::quit(bool kill)
{
    if (!kill) {
        #ifdef USE_LOADGEN_GUI
        QPixmap pixmap(":/waitforcallsclosing.png");
        QSplashScreen splash(pixmap);
        splash.show();
        #endif // USE_LOADGEN_GUI
        terminateAllCalls();
        waitForThreadsToTerminate();
    }
    exit(0);
}

void Oxsipgen::waitForThreadsToTerminate()
{
    #ifdef USE_LOADGEN_GUI
    // this is not really a proper way to wait for the threads, 
    for(int i=0; i<getNumberOfThreads();i++) {   // wait for threads to finish
        scArray.at(i)->wait();
    }
    #else
    thrds.join_all();
    #endif // USE_LOADGEN_GUI
}


#ifdef USE_LOADGEN_GUI
void Oxsipgen::forceHangup()
{
    terminateAllCalls();
}
void Oxsipgen::changeAudioThread (const int thread)
{
    //std::cerr << "Oxsipgen::changeAudioThread (const int thread)" <<endl;
    setAudioThread(thread);
}

void Oxsipgen::runSlot()
{
    this->start();
}
void Oxsipgen::loadNewSceneSlot( const QString filename )
{
    this->loadNewScene( filename.toStdString() );
}

void Oxsipgen::setQApplication(QApplication* app)
{
    p_app = app;
    QObject::connect( app, SIGNAL(lastWindowClosed()), 
                      this, SLOT(quit()) );
}

void Oxsipgen::quitkill()
{
    quit(true);
    p_app->quit();
}
void Oxsipgen::quitfriendly()
{
    quit(false);
    p_app->quit();
}

void Oxsipgen::updateStatistics()
{
    ui.lcdNumber_simulCalls->display(static_cast<int>(getNumberOfThreads()));
    ui.lcdNumber_startedCalls->display(SingleCall::getNumberOfStartedCalls());
    ui.lcdNumber_badEndedCalls->display(SingleCall::getNumberOfBadEndedCalls());
    ui.progressBar_percGoodCalls->setValue(SingleCall::getPercentageGoodCalls());
}

#endif // USE_LOADGEN_GUI




